#include "stdafx.h"

#include "file/file_manager.h"

#include "texture/texture_manager.h"

#include "renderer/renderer.h"
#include "renderer/framebuffer.h"
#include "renderer/ARB_extensions.h"
#include "renderer/helpers.h"

#include "octree/octree_manager.h"
#include "octree/node.h"

#include "objects/entity.h"

#include "random.h"

#include <boost/smart_ptr/shared_ptr.hpp>
#include <vector>

#include "ui/TextOutput.h"

#include "timer.h"

void drawCube()
{
	glBegin(GL_QUADS);
	{
		glColor3f(1.0f, 0.0f, 0.0f);
		glTexCoord2d(0.0,0.0); glVertex3f(-1.0f, -1.0f, 1.0f);
		glTexCoord2d(1.0,0.0); glVertex3f(1.0f, -1.0f, 1.0f);
		glTexCoord2d(1.0,1.0); glVertex3f(1.0f, 1.0f, 1.0f);
		glTexCoord2d(0.0,1.0); glVertex3f(-1.0f, 1.0f, 1.0f);

		glColor3f(0.0f, 1.0f, 0.0f);
		glTexCoord2d(0.0,0.0); glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2d(1.0,0.0); glVertex3f(1.0f, -1.0f, 1.0f);
		glTexCoord2d(1.0,1.0); glVertex3f(1.0f, 1.0f, 1.0f);
		glTexCoord2d(0.0,1.0); glVertex3f(1.0f, 1.0f, -1.0f);

		glColor3f(0.0f, 0.0f, 1.0f);
		glTexCoord2d(0.0,0.0); glVertex3f(-1.0f, 1.0f, -1.0f);
		glTexCoord2d(1.0,0.0); glVertex3f(1.0f, 1.0f, -1.0f);
		glTexCoord2d(1.0,1.0); glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2d(0.0,1.0); glVertex3f(-1.0f, -1.0f, -1.0f);

		glColor3f(1.0f, 1.0f, 0.0f);
		glTexCoord2d(0.0,0.0); glVertex3f(-1.0f, 1.0f, -1.0f);
		glTexCoord2d(1.0,0.0); glVertex3f(-1.0f, 1.0f, 1.0f);
		glTexCoord2d(1.0,1.0); glVertex3f(-1.0f, -1.0f, 1.0f);
		glTexCoord2d(0.0,1.0); glVertex3f(-1.0f, -1.0f, -1.0f);

		glColor3f(1.0f, 0.0f, 1.0f);
		glTexCoord2d(0.0,0.0); glVertex3f(-1.0f, 1.0f, 1.0f);
		glTexCoord2d(1.0,0.0); glVertex3f(1.0f, 1.0f, 1.0f);
		glTexCoord2d(1.0,1.0); glVertex3f(1.0f, 1.0f, -1.0f);
		glTexCoord2d(0.0,1.0); glVertex3f(-1.0f, 1.0f, -1.0f);

		glColor3f(0.0f, 1.0f, 1.0f);
		glTexCoord2d(0.0,0.0); glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2d(1.0,0.0); glVertex3f(1.0f, -1.0f, -1.0f);
		glTexCoord2d(1.0,1.0); glVertex3f(1.0f, -1.0f, 1.0f);
		glTexCoord2d(0.0,1.0); glVertex3f(-1.0f, -1.0f, 1.0f);
	}
	glEnd();
}

static bool	g_demo_draw_entities = true;
static bool	g_demo_draw_octree = true;
static bool	g_demo_draw_collisions = true;
static bool g_demo_rotate = true;

void demo_toggle_entities()
{
	g_demo_draw_entities =! g_demo_draw_entities;
}
void demo_toggle_collisions()
{
	g_demo_draw_collisions =! g_demo_draw_collisions;
}
void demo_toggle_octree()
{
	g_demo_draw_octree =! g_demo_draw_octree;
}

void demo_toggle_rotation()
{
	g_demo_rotate =! g_demo_rotate;
}


static GLuint texture;
static boost::shared_ptr<CFrameBufferObject> _fbPtr;

static const float g_rot_inc = 0.5f;

std::vector<CPlane> planesList;

void mkPlanes(void)
{
	const float speed_factor = 0.008f;
	CRandom rgen(0xdeadbabe);
	srand(0xbabeb00b);

	for (int i = 0; i < 100; i++)
	{
		CPlane pl(i, &CEntityManager::planes);
		
		float x = (float)((double)rand() / (RAND_MAX + 1) * (190.0f - -190.0f) + -190.0f);
		float y = (float)((double)rand() / (RAND_MAX + 1) * (190.0f - -190.0f) + -190.0f);
		float z = (float)((double)rand() / (RAND_MAX + 1) * (190.0f - -190.0f) + -190.0f);

		float tx = (float)((double)rand() / (RAND_MAX + 1) * (speed_factor - -speed_factor) + -speed_factor);
		float ty = (float)((double)rand() / (RAND_MAX + 1) * (speed_factor - -speed_factor) + -speed_factor);
		float tz = (float)((double)rand() / (RAND_MAX + 1) * (speed_factor - -speed_factor) + -speed_factor);

		pl.position() = _mm_set_ps(x, y, z, 1.5f);
		pl.orientation() = _mm_set_ps(tx, ty, tz, 0.0f);

//		pl.position() = _mm_set_ps(rgen.rnd(-100.0f, 100.0f), rgen.rnd(-100.0f,100.0f), rgen.rnd(-100.0f, 100.0f), 0.2f);
//		pl.orientation() = _mm_set_ps(rgen.rnd(-speed_factor, speed_factor), rgen.rnd(-speed_factor, speed_factor), rgen.rnd(-speed_factor, speed_factor), 0.0f);

		planesList.push_back(pl);
	}
}

void mkPlanes3(void)
{
	CPlane pl0(0, &CEntityManager::planes);
	pl0.position() = _mm_set_ps(120.0f, 120.0f, 105.0f, 10.0f);
	pl0.orientation() = _mm_set_ps(-0.5f, -0.5f, -0.5f, 0.0f);
	planesList.push_back(pl0);
}

void mkPlanes2(void)
{
	CPlane pl0(0, &CEntityManager::planes);
	CPlane pl1(1, &CEntityManager::planes);
	CPlane pl2(2, &CEntityManager::planes);
	CPlane pl3(3, &CEntityManager::planes);
	CPlane pl4(4, &CEntityManager::planes);
	CPlane pl5(5, &CEntityManager::planes);
	CPlane pl6(6, &CEntityManager::planes);
	CPlane pl7(7, &CEntityManager::planes);

	const float speed_factor = 0.8f;

	pl0.position() = _mm_set_ps(100.0f, 100.0f, 100.0f, 10.0f);
	pl0.orientation() = _mm_set_ps(-speed_factor, -speed_factor, -speed_factor, 0.0f);

	pl1.position() = _mm_set_ps(100.0f, 100.0f, -100.0f, 10.0f);
	pl1.orientation() = _mm_set_ps(-speed_factor, -speed_factor, speed_factor, 0.0f);

	pl2.position() = _mm_set_ps(100.0f, -100.0f, 100.0f, 10.0f);
	pl2.orientation() = _mm_set_ps(-speed_factor, speed_factor, -speed_factor, 0.0f);

	pl3.position() = _mm_set_ps(100.0f, -100.0f, -100.0f, 10.0f);
	pl3.orientation() = _mm_set_ps(-speed_factor, speed_factor, speed_factor, 0.0f);

	pl4.position() = _mm_set_ps(-100.0f, 100.0f, 100.0f, 10.0f);
	pl4.orientation() = _mm_set_ps(speed_factor, -speed_factor, -speed_factor, 0.0f);

	pl5.position() = _mm_set_ps(-100.0f, 100.0f, -100.0f, 10.0f);
	pl5.orientation() = _mm_set_ps(speed_factor, -speed_factor, speed_factor, 0.0f);

	pl6.position() = _mm_set_ps(-100.0f, -100.0f, 100.0f, 10.0f);
	pl6.orientation() = _mm_set_ps(speed_factor, speed_factor, -speed_factor, 0.0f);

	pl7.position() = _mm_set_ps(-100.0f, -100.0f, -100.0f, 10.0f);
	pl7.orientation() = _mm_set_ps(speed_factor, speed_factor, speed_factor, 0.0f);

	planesList.clear();
	planesList.push_back(pl0);
	planesList.push_back(pl1);
	planesList.push_back(pl2);
	planesList.push_back(pl3);
	planesList.push_back(pl4);
	planesList.push_back(pl5);
	planesList.push_back(pl6);
	planesList.push_back(pl7);
}

void client_init()
{
//	const octree::COCTree * ocnode = octree::COCTreeManager::getRoot();
	CFontManager::get()->ActivateFont();
	
	/*
	for (int i = 0; i < 8; i++)
	{
		ocnode->getOctant(i).subdivide_();

		for (int j = 0; j < 8; j++)
		{
			ocnode->getOctant(i).getOctant(j).subdivide_();
			for (int k = 0; k < 8; k++)
			{
				ocnode->getOctant(i).getOctant(j).getOctant(k).subdivide_();

				for (int l = 0; l < 8; l++)
					ocnode->getOctant(i).getOctant(j).getOctant(k).getOctant(l).subdivide_();

			}
		}
	}
	*/
	_fbPtr.reset(new CFrameBufferObject(SRect(0, 0, 256, 256)));

	glEnable(GL_TEXTURE_2D);
	texture = CTextureManager::getInstance()->loadTexture("../test.bmp");

	mkPlanes();
	octree::COCTreeManager::getRoot()->registerEntities(&planesList[0], planesList.size());

	CRenderer::getInstance()->getViewport()->activate();
	glBindTexture(GL_TEXTURE_2D, 0);
//	glClearColor(0.01f, 0.01f, 0.01f, 1.0f);
	glClearColor(0.8f, 0.8f, 0.9f, 1.0f);
}

void client_render_fb()
{
	static float dd = 0.0f;
	dd += 0.01f;

	_fbPtr->enable(); 
	glBindTexture(GL_TEXTURE_2D, texture);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glTranslatef(0.0f, 0.0f, -4.0f);
	glRotatef(dd, 1.0f, 1.0f, 1.0f);
	drawCube();

	_fbPtr->release();

	CRenderer::getInstance()->getViewport()->activate();

	glLoadIdentity();

	glTranslatef(0.0f, 0.0f, -4.0f);
	glRotatef(dd, 1.0f, 1.0f, 1.0f);

	/*
	glBindTexture(GL_TEXTURE_2D, 0);
	drawCube();
	*/

	glBindTexture(GL_TEXTURE_2D, _fbPtr->getTexture());

	drawCube();

	glBindTexture(GL_TEXTURE_2D, 0);
	if (dd > 360.0f) dd = 0.0f;
}

void client_render_grid()
{
	glPushMatrix();
	{
		//		glTranslatef(0.0f, 0.0f, -4.0f);
		//		glRotatef(dd, 1.0f, 1.0f, 1.0f);
		helpers::Grid::draw();
	}
	glPopMatrix();
}

void render_bbox(const __m128 & center)
{
	glPushMatrix();
	{
		const float scale_factor = center.m128_f32[0] * 2.0f;
		glTranslatef(center.m128_f32[3], center.m128_f32[2], center.m128_f32[1]);
		glScalef(scale_factor, scale_factor, scale_factor);
		helpers::Cube::draw();
	}
	glPopMatrix();
}

void client_render_entities_bbox(const DataBlock<CVector3> & positions, const unsigned long count)
{
	for (unsigned long i = 0; i < count; i++)
		render_bbox(positions[i].getXMM());
}

static unsigned long sgn_mask = 0x7FFFFFFF;
static unsigned long sgn_invmask = 0x80000000;
static const __m128 g_sgn_mask_vct = _mm_load1_ps(reinterpret_cast<float *>(&sgn_mask));
static const __m128 g_sgn_invmask_vct = _mm_load1_ps(reinterpret_cast<float *>(&sgn_invmask));
static const __m128 g_maxval_vct = _mm_set1_ps(200.0f);
static const float g_accel = 0.24f;

void client_update_entities()
{
	for (std::vector<CPlane>::iterator it = planesList.begin(); it != planesList.end(); it++)
	{
		const __m128 pos = (*it).position().getXMM();
		const __m128 dir = (*it).orientation().getXMM();
		const __m128 sum = _mm_add_ps(pos, dir);
		const __m128 psum = _mm_and_ps(g_sgn_mask_vct, sum);
		const __m128 dif = _mm_sub_ps(g_maxval_vct, psum);
		const __m128 signs = _mm_and_ps(g_sgn_invmask_vct, dif);

		(*it).orientation().getXMM() = _mm_xor_ps(dir, signs);
		(*it).position() += (*it).orientation().getXMM();
	}
}

void client_render()
{
	static char buff[256];
	static float dd = 0.0f;

	if (g_demo_rotate)
		dd += g_rot_inc;

	CTimer timer;
	
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	client_update_entities();

	octree::COCTreeManager::updateActiveNodes();

	glTranslatef(100.0f, 100.0f, -1000.0f);

	glRotatef(dd, 1.0f, 1.0f, 1.0f);
	if (dd > 360.0f) dd = 0.0f;

	//	client_render_grid();
	glColor4f(0.1f, 0.1f, 0.1f, 0.1f);
	if (g_demo_draw_octree)
		octree::COCTreeManager::drawActiveNodes(render_bbox);
	octree::COCTreeManager::getRoot()->drawNode(render_bbox, false);

	if (g_demo_draw_entities)
	{
		glColor4f(0.2f, 0.4f, 0.8f, 0.4f);
		client_render_entities_bbox(CEntityManager::planes.position, planesList.size());
	}

	if (g_demo_draw_collisions)
	{
		glColor4f(0.8f, 0.1f, 0.1f, 0.4f);
		const std::vector<std::pair<CEntity *, CEntity *> > & collisions = octree::COCTreeManager::getCollidingEntities();
		for (std::vector<std::pair<CEntity *, CEntity *> >::const_iterator it = collisions.begin(); it != collisions.end(); it++)
		{
			render_bbox((*it).first->position().getXMM());
			render_bbox((*it).second->position().getXMM());
		}
	}

	glLoadIdentity();
	glTranslatef(100.0f, 100.0f, -1000.0f);
	glColor4f(0.3f, 0.4f, 0.8f, 0.4f);

	const float text_x_offset = -500.0f;

	sprintf(buff, "fps: %4.0f                         ", 1.0/timer.getElapsedTime());
	CFontManager::get()->print_line_at(buff, 20, text_x_offset, 260.0f);

	sprintf(buff, "collisions: %i                     ", octree::COCTreeManager::getCollidingEntities().size());
	CFontManager::get()->print_line_at(buff, 20, text_x_offset, 280.0f);

	sprintf(buff, "entities: %i                       ", octree::COCTreeManager::getRoot()->getEntityCount());
	CFontManager::get()->print_line_at(buff, 20, text_x_offset, 300.0f);
	if (g_demo_draw_entities)
		glColor4f(0.1f, 0.1f, 0.1f, 0.9f);
	else
		glColor4f(0.1f, 0.1f, 0.1f, 0.2f);
	CFontManager::get()->print_line_at("F1 - Draw Entities", 18, text_x_offset, 220.0f);

	if (g_demo_draw_collisions)
		glColor4f(0.1f, 0.1f, 0.1f, 0.9f);
	else
		glColor4f(0.1f, 0.1f, 0.1f, 0.2f);
	CFontManager::get()->print_line_at("F2 - Draw Collisions", 20, text_x_offset, 200.0f);

	if (g_demo_draw_octree)
		glColor4f(0.1f, 0.1f, 0.1f, 0.9f);
	else
		glColor4f(0.1f, 0.1f, 0.1f, 0.2f);
	CFontManager::get()->print_line_at("F3 - Draw octree", 16, text_x_offset, 180.0f);

	if (g_demo_rotate)
		glColor4f(0.1f, 0.1f, 0.1f, 0.9f);
	else
		glColor4f(0.1f, 0.1f, 0.1f, 0.2f);
	CFontManager::get()->print_line_at("F4 - Toggle spinning", 20, text_x_offset, 160.0f);
		//	client_render_fb();
}